﻿using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using UnityEngine;

namespace LBNet
{
    public class Connector
    {
        public event EventHandler<EventArgs> OnConnected;
        public event EventHandler<EventArgs> OnConnectFailed;
        public event EventHandler<EventArgs> OnDisconnected;
        public event EventHandler<NetEventArg_Received> OnReceived;

        private TcpClient m_TcpClient = null;                             // tcp client
        private NetworkStream m_NetStream = null;                             // network stream
        private IPAddress m_ServerIpAdd = null;                             // ip
        private int m_nServerPort = 0;                                // port

        private eNetState m_eCurNetState = eNetState.NS_None;                 // state of the net
        private BufferInfo m_BufferInfo = new BufferInfo();                 // info of the buffer
        private byte[] m_byHead = new byte[NetDefine.HEAD_LEN_SIZE];  // buffer of the head
        private byte[] m_byReceiveBuf = new byte[NetDefine.BUFFER_LENGTH];// buffer of the content

        public Connector(string sIP, int nPort)
        {
            m_ServerIpAdd = IPAddress.Parse(sIP);
            m_nServerPort = nPort;
        }


        //属性
        public eNetState NetState
        {
            get { return m_eCurNetState; }
        }

        void _NetDrop(eNetError reason)
        {

            switch (reason)
            {
                case eNetError.NE_ReadBufferOverflow:
                    PLDebug.LogWarning("读取数据包长度不合法（不大于0或者超出缓冲区长度）, 自动断开连接");
                    break;
                case eNetError.NE_WriteBufferOverflow:
                    PLDebug.LogWarning("写入数据包长度不合法（不大于0或者超出缓冲区长度）, 自动断开连接");
                    break;
                case eNetError.NE_OutOfWorking:
                    PLDebug.LogWarning("网络无法工作, 自动断开连接");
                    m_eCurNetState = eNetState.NS_AgaionConnect;
                    break;
                case eNetError.NE_WriteException:
                    PLDebug.LogWarning("网络写入异常, 自动断开连接");
                    break;

            }
            
            m_eCurNetState = eNetState.NS_ConnectDropped;

            //this.CloseConnect();
        }

        public void Update()
        {

            switch (m_eCurNetState)
            {
                case eNetState.NS_None:
                    {
                        // do nothing
                    }
                    break;
                case eNetState.NS_Connecting:
                    {
                        // just wait
                    }
                    break;
                case eNetState.NS_ConnectSuccessed:
                    {
                        m_eCurNetState = eNetState.NS_Working;

                        if (OnConnected != null)
                            OnConnected(this, null);
                    }
                    break;
                case eNetState.NS_ConnectFailed:
                    {
                        CloseConnect();

                        if (OnConnectFailed != null)
                            OnConnectFailed(this, null);
                    }
                    break;
                case eNetState.NS_Working:
                    {
                        // do nothing
                    }
                    break;
                case eNetState.NS_ConnectDropped:
                    {
                        CloseConnect();

                        if (OnDisconnected != null)
                            OnDisconnected(this, null);
                    }
                    break;
                case eNetState.NS_AgaionConnect:
                    {

                        m_TcpClient.Close();
                        NewTcpClient();

                    }
                    break;
                default:
                    NetTool.LogError("unreachable..");
                    break;
            }
        }
        public bool IsStopped()
        {
            return NetState == eNetState.NS_None;
        }
        public bool IsWorking()
        {
            return NetState == eNetState.NS_Working;
        }
        public void StartConnect()
        {
            if (m_eCurNetState != eNetState.NS_None)
            {
                NetTool.LogWarning("Can not connect, because the netstate is" + m_eCurNetState.ToString());
                return;
            }

            m_eCurNetState = eNetState.NS_Connecting;
            NetTool.Log("Connect to " + m_ServerIpAdd + ":" + m_nServerPort);

            NewTcpClient();

        }

        private void NewTcpClient()
        {
            m_TcpClient = new TcpClient
            {
                ReceiveBufferSize = NetDefine.BUFFER_LENGTH,
                SendBufferSize = NetDefine.BUFFER_LENGTH
            };
            m_TcpClient.BeginConnect(m_ServerIpAdd, m_nServerPort, new AsyncCallback(ConnectCallback), null);
        }
        public void CloseConnect()
        {
            if (m_eCurNetState == eNetState.NS_None)
            {
                NetTool.LogWarning("There's no connection to close!");
                return;
            }

            m_eCurNetState = eNetState.NS_None;
            NetTool.Log("Close connection from " + m_ServerIpAdd + ":" + m_nServerPort);

            m_NetStream = null;

            m_TcpClient.Close();
            m_TcpClient = null;
        }

        private void ConnectCallback(IAsyncResult result)
        {
            if (!result.IsCompleted)
                return;

            m_TcpClient.EndConnect(result);

            if (m_TcpClient.Connected)
            {
                m_NetStream = m_TcpClient.GetStream();

                ReceiveHeadLen();

                m_eCurNetState = eNetState.NS_ConnectSuccessed;

                NetTool.Log("Connecting successed.");
            }
            else
            {
                m_eCurNetState = eNetState.NS_ConnectFailed;

                NetTool.Log("Connecting failed.");
            }
        }

        private void ReceiveHeadLen()
        {
            m_BufferInfo.nBufferSize = NetDefine.HEAD_LEN_SIZE;
            m_BufferInfo.nReadSize = 0;

            m_NetStream.BeginRead(m_byHead, 0, NetDefine.HEAD_LEN_SIZE, new AsyncCallback(HeadLenCallBack), null);
        }

        private void HeadLenCallBack(IAsyncResult result)
        {
            m_BufferInfo.nReadSize += m_NetStream.EndRead(result);

            // 如果包头没有读够数据那么就继续读取
            if (m_BufferInfo.nReadSize < m_BufferInfo.nBufferSize)
            {
                m_NetStream.BeginRead(m_byHead, m_BufferInfo.nReadSize, m_BufferInfo.nBufferSize - m_BufferInfo.nReadSize, new AsyncCallback(HeadLenCallBack), null);
            }
            else
            {
                if (!BitConverter.IsLittleEndian)
                    Array.Reverse(m_byHead);
                int nBufferSize = (int)BitConverter.ToUInt32(m_byHead, 0) + NetDefine.HEAD_OP_SIZE;
                m_BufferInfo.nBufferSize = nBufferSize;
                m_BufferInfo.nReadSize = 0;
                if (nBufferSize > 0 && nBufferSize < NetDefine.BUFFER_LENGTH)
                {
                    m_NetStream.BeginRead(m_byReceiveBuf, 0, nBufferSize, new AsyncCallback(ReceiveCallBack), null);
                }
                else
                {
                    _NetDrop(eNetError.NE_ReadBufferOverflow);
                }
            }
        }

        private void ReceiveCallBack(IAsyncResult result)
        {
            m_BufferInfo.nReadSize += m_NetStream.EndRead(result);

            // 如果没有读够数据那么就继续读取
            if (m_BufferInfo.nReadSize < m_BufferInfo.nBufferSize)
            {
                m_NetStream.BeginRead(m_byReceiveBuf, m_BufferInfo.nReadSize, m_BufferInfo.nBufferSize - m_BufferInfo.nReadSize, new AsyncCallback(ReceiveCallBack), null);
            }
            else
            {
                if (OnReceived != null)
                    OnReceived(this, new NetEventArg_Received(m_byReceiveBuf, m_BufferInfo.nReadSize));

                this.ReceiveHeadLen();
            }
        }

        public void SendToServer(byte[] byData)
        {
            if (m_eCurNetState != eNetState.NS_Working)
            {
                _NetDrop(eNetError.NE_OutOfWorking);
                return;
            }

            try
            {
                m_NetStream.BeginWrite(byData, 0, byData.Length, null, null);
            }
            catch (Exception e)
            {
                PLDebug.LogError(e.ToString());
                _NetDrop(eNetError.NE_WriteException);
            }

            //int length = byData.Length;
            //if (length > 0 && length < NetDefine.BUFFER_LENGTH)
            //{
            //    byte[] _data = new byte[length + NetDefine.HEAD_LENGTH];
            //    Array.Copy(BitConverter.GetBytes(length - NetDefine.OPCODE_LENGTH), 0, _data, 0, NetDefine.HEAD_LENGTH);
            //    Array.Copy(byData, 0, _data, NetDefine.HEAD_LENGTH, byData.Length);
            //    try
            //    {
            //        m_NetStream.BeginWrite(_data, 0, length + NetDefine.HEAD_LENGTH, null, null);
            //        return;
            //    }
            //    catch// (Exception e)
            //    {
            //        //NetTool.LogWarning("Write Error:" + e.ToString());
            //        _NetDrop(eNetError.NE_WriteException);
            //    }
            //}
            //else
            //{
            //    _NetDrop(eNetError.NE_WriteBufferOverflow);
            //}
        }
    }

}   // end namespace